/**
  ******************************************************************************
  * @file    bsp_eth_uip_driver.c
  * @author  Iron
  * @date    2022-01-01
  * @version v1.0
  * @brief   bsp_eth_uip_driver c file
  */

/** @addtogroup GROUP_TEMPLATE
  * @{
  */

/* Private includes ----------------------------------------------------------*/
#include "bsp_eth.h"
#include "contiki.h"
#include "contiki-net.h"
#include "bsp_eth_contiki_driver.h"

/*---------------------------------------------------------------------------*/
/* Log configuration */
#include "sys/log.h"
#define LOG_MODULE "ETH"
#define LOG_LEVEL LOG_LEVEL_MAC

/* Private define ------------------------------------------------------------*/
#define ETH_RX_EVT  0x01
#define ETH_TX_EVT  0x02

#define BUF(buf)             ((struct uip_eth_hdr *)&(buf)[0])
#define IPBUF(buf)           ((uint8_t *)&(buf)[14]) // ethernet head 14

/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

PROCESS(ethernet_process, "Ethernet driver");

#define ETH_TX_BUFSIZE  UIP_BUFSIZE
#define ETH_RX_BUFSIZE (UIP_BUFSIZE * 2)

static volatile uint16_t eth_rxbuf_in = 0;
__ALIGNED(4) static uint8_t eth_rxbuf[ETH_RX_BUFSIZE];
__ALIGNED(4) static uint8_t eth_txbuf[ETH_TX_BUFSIZE];

/* Private function prototypes -----------------------------------------------*/

/*---------------------------------------------------------------------------*/
void bsp_eth_recv_irq_handle(uint16_t rxlen)
{
    uint32_t post_data = (eth_rxbuf_in << 16) | rxlen;

    process_post(&ethernet_process, ETH_RX_EVT, (void *)post_data);

    eth_rxbuf_in += rxlen;

    if (eth_rxbuf_in & 0x03)
    {
        eth_rxbuf_in &= ~0x03;
        eth_rxbuf_in += 0x04;   // 4 字节对齐
    }

    if ((eth_rxbuf_in + UIP_BUFSIZE) > ETH_RX_BUFSIZE)
    {
        eth_rxbuf_in = 0;
    }

    bsp_eth_rx_enable((uint32_t)&eth_rxbuf[eth_rxbuf_in], UIP_BUFSIZE);
}

/*---------------------------------------------------------------------------*/
static uint8_t
output(const linkaddr_t *localdest)
{
    uint16_t tx_len = uip_len + sizeof(struct uip_eth_hdr);
    
    while(bsp_eth_get_tx_status() == BSP_ETH_ENABLE);

    if (localdest == NULL || linkaddr_cmp(localdest, &linkaddr_null))
    {
        /* broadcast packet */
        memset(&BUF(eth_txbuf)->dest, 0xFF, sizeof(struct uip_eth_addr));
    }
    else
    {
        memcpy(&BUF(eth_txbuf)->dest, localdest, sizeof(struct uip_eth_addr));
    }

    BUF(eth_txbuf)->type = UIP_HTONS(UIP_ETHTYPE_IPV6);

    memcpy(&BUF(eth_txbuf)->src, uip_lladdr.addr, sizeof(struct uip_eth_addr));

    memcpy(IPBUF(eth_txbuf), uip_buf, uip_len);

    bsp_eth_tx_enable((uint32_t)eth_txbuf, tx_len);

//    LOG_DBG("eth tx  ip: src=");
//    LOG_DBG_6ADDR(&UIP_IP_BUF->srcipaddr);
//    LOG_OUTPUT(" / dst=");
//    LOG_DBG_6ADDR(&UIP_IP_BUF->destipaddr);
//    LOG_OUTPUT("\n");
//    LOG_DBG("eth tx mac: src=");
//    LOG_DBG_LLADDR((linkaddr_t *)(&BUF(eth_txbuf)->src));
//    LOG_OUTPUT(" / dst=");
//    LOG_DBG_LLADDR((linkaddr_t *)(&BUF(eth_txbuf)->dest));
//    LOG_OUTPUT("\n");

    return 0;
}

/*---------------------------------------------------------------------------*/
static void
input(void)
{
    if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
    {
        if (eth_rxbuf_in & 0x03)
        {
            eth_rxbuf_in &= ~0x03;
            eth_rxbuf_in += 0x04;   // 4 字节对齐
        }

        if ((eth_rxbuf_in + UIP_BUFSIZE) > ETH_RX_BUFSIZE)
        {
            eth_rxbuf_in = 0;
        }

        bsp_eth_rx_enable((uint32_t)&eth_rxbuf[eth_rxbuf_in], UIP_BUFSIZE);
    }
}

/*---------------------------------------------------------------------------*/
static void
init(void)
{
    static bool isinit = false;

    if (isinit)
        return;

    isinit = true;

//    uint8_t eui64[8];
//
//    /* Assume that linkaddr_node_addr holds the EUI64 of this device. */
//    memcpy(eui64, &linkaddr_node_addr, sizeof(eui64));
//
//    /* Mangle the EUI64 into a 48-bit Ethernet address. */
//    memcpy(&uip_lladdr.addr[0], &eui64[0], 3);
//    memcpy(&uip_lladdr.addr[3], &eui64[5], 3);

    /* In case the OUI happens to contain a broadcast bit, we mask that
       out here. */
    uip_lladdr.addr[0] = (uip_lladdr.addr[0] & 0xfe);

    /* Set the U/L bit, in order to create a locally administered MAC address */
    uip_lladdr.addr[0] = (uip_lladdr.addr[0] | 0x02);

    bsp_eth_init(uip_lladdr.addr);

    // printf(s, "%#0*X", 8, 128); //产生"0X000080"，"#"产生0X
    LOG_INFO("Ethenet MAC Addr %02X:%02X:%02X:%02X:%02X:%02X\n",
             uip_lladdr.addr[0], uip_lladdr.addr[1], uip_lladdr.addr[2],
             uip_lladdr.addr[3], uip_lladdr.addr[4], uip_lladdr.addr[5]
            );

    /*
     * Set out output function as the function to be called from uIP to
     * send a packet.
     */
//    tcpip_set_outputfunc(uip_output);

    process_start(&ethernet_process, NULL);
}

/*---------------------------------------------------------------------------*/
PROCESS_THREAD(ethernet_process, ev, data)
{
    //static struct etimer e;

    PROCESS_BEGIN();

    if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
    {
        input();
    }

    while (1)
    {
        //etimer_set(&e, 1);
        PROCESS_WAIT_EVENT();

        switch (ev)
        {
        case ETH_RX_EVT:
        {
            uint16_t rx_index = (((uint32_t)data) >> 16) & 0xFFFF;
            uint16_t rx_len = ((uint32_t)data) & 0xFFFF;
            uint8_t *rxbuf = &eth_rxbuf[rx_index];

            if (rx_len > 0 && rx_len <= UIP_BUFSIZE)
            {
                struct uip_eth_hdr *eth_head = (struct uip_eth_hdr *)&rxbuf[0];

                if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
                {
                    input(); // 4字节对齐
                }

                if (eth_head->type == UIP_HTONS(UIP_ETHTYPE_IPV6))
                {
                    uip_len = rx_len - sizeof(struct uip_eth_hdr);
                    memcpy(uip_buf, IPBUF(rxbuf), uip_len);

//                    LOG_DBG("eth rx  ip: src=");
//                    LOG_DBG_6ADDR(&UIP_IP_BUF->srcipaddr);
//                    LOG_OUTPUT(" / dst=");
//                    LOG_DBG_6ADDR(&UIP_IP_BUF->destipaddr);
//                    LOG_OUTPUT("\n");
//                    LOG_DBG("eth tx mac: src=");
//                    LOG_DBG_LLADDR((linkaddr_t *)(&BUF(rxbuf)->src));
//                    LOG_OUTPUT(" / dst=");
//                    LOG_DBG_LLADDR((linkaddr_t *)(&BUF(rxbuf)->dest));
//                    LOG_OUTPUT("\n");

                    tcpip_input();
                }
            }
        }
        break;

        case ETH_TX_EVT:
            break;

        default:
            break;
        }
    }

    PROCESS_END();
}

/*---------------------------------------------------------------------------*/
const struct network_driver uip_ethernet_driver =
{
    "eth",
    init,
    input,
    output
};

static int
uip_forward_output(void)
{
    // openwrt br-lan mac FA:F0:6A:39:DC:07
    const linkaddr_t dest =
    {
        .u8 = {0xFA, 0xF0, 0x6A, 0x39, 0xDC, 0x07}
    };

    return output(&dest);
}

const struct uip_fallback_interface uip_fw_driver =
{
    init,
    uip_forward_output
};


/**
  * @}
  */

/******************* (C)COPYRIGHT 2022 ***** END OF FILE *********************/
